package com.english.utils;

import java.security.MessageDigest;
import java.util.Map;
import java.util.TreeMap;

/**
 * 微信支付V2 API签名验证器
 * 严格按照微信官方文档要求实现
 */
public class WechatPayV2SignatureValidator {
    
    public static void main(String[] args) {
        System.out.println("=== 微信支付V2签名严格验证 ===");
        
        // 使用实际的参数进行测试
        validateWechatPaySignature();
    }
    
    public static void validateWechatPaySignature() {
        // 1. 按照你的实际配置设置参数
        String appId = "wx2f9ca3ee9556234f";
        String mchId = "1724666825";
        String apiKey = "18308909937or18970222333Oscar168";
        String openId = "opMFNvowB-lDGIOGm7cCewHVRTG4";
        
        // 2. 构建请求参数（按照微信支付统一下单接口要求）
        Map<String, String> params = new TreeMap<>();
        params.put("appid", appId);
        params.put("body", "测试商品");
        params.put("mch_id", mchId);
        params.put("nonce_str", "test123456789abcdef");
        params.put("notify_url", "https://test.com/notify");
        params.put("openid", openId);
        params.put("out_trade_no", "TEST" + System.currentTimeMillis());
        params.put("sign_type", "MD5");
        params.put("spbill_create_ip", "127.0.0.1");
        params.put("total_fee", "1"); // 1分
        params.put("trade_type", "JSAPI");
        
        System.out.println("原始参数:");
        params.forEach((k, v) -> System.out.println(k + " = " + v));
        
        // 3. 按照微信要求进行签名
        String signature = generateWechatSign(params, apiKey);
        System.out.println("\n生成的签名: " + signature);
        
        // 4. 验证签名长度和格式
        System.out.println("签名长度: " + signature.length());
        System.out.println("签名格式: " + (signature.matches("[A-F0-9]{32}") ? "正确" : "错误"));
        
        // 5. 验证API密钥
        System.out.println("\nAPI密钥验证:");
        System.out.println("API密钥: " + apiKey);
        System.out.println("密钥长度: " + apiKey.length());
        System.out.println("密钥格式: " + (apiKey.length() == 32 ? "长度正确" : "长度错误"));
        
        // 6. 检查关键参数
        System.out.println("\n关键参数检查:");
        checkParameter("appid", appId);
        checkParameter("mch_id", mchId);
        checkParameter("openid", openId);
        
        // 7. 构建完整的XML请求
        params.put("sign", signature);
        String xml = buildXmlRequest(params);
        System.out.println("\n完整XML请求:");
        System.out.println(xml);
    }
    
    private static String generateWechatSign(Map<String, String> params, String apiKey) {
        // 1. 排序参数（TreeMap已自动排序）
        StringBuilder stringBuilder = new StringBuilder();
        
        // 2. 拼接参数字符串
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (entry.getValue() != null && !entry.getValue().isEmpty() && !"sign".equals(entry.getKey())) {
                stringBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        
        // 3. 添加密钥
        stringBuilder.append("key=").append(apiKey);
        
        String stringToSign = stringBuilder.toString();
        System.out.println("\n待签名字符串:");
        System.out.println(stringToSign);
        
        // 4. MD5加密
        return md5(stringToSign).toUpperCase();
    }
    
    private static String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(input.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte b : array) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException("MD5加密失败", e);
        }
    }
    
    private static void checkParameter(String name, String value) {
        System.out.println(name + ": " + value + " (长度: " + value.length() + ")");
    }
    
    private static String buildXmlRequest(Map<String, String> params) {
        StringBuilder xml = new StringBuilder("<xml>");
        for (Map.Entry<String, String> entry : params.entrySet()) {
            xml.append("<").append(entry.getKey()).append("><![CDATA[")
               .append(entry.getValue()).append("]]></").append(entry.getKey()).append(">");
        }
        xml.append("</xml>");
        return xml.toString();
    }
} 